/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License,Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.mylibrary.player.view;

import ohos.aafwk.ability.Ability;
import ohos.agp.animation.AnimatorProperty;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.DependentLayout;
import ohos.agp.components.DirectionalLayout;
import ohos.agp.components.Image;
import ohos.agp.components.LayoutScatter;
import ohos.agp.components.Slider;
import ohos.agp.components.Text;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.window.service.WindowManager;
import ohos.app.Context;
import ohos.bundle.AbilityInfo;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.eventhandler.InnerEvent;

import com.mylibrary.ResourceTable;
import com.mylibrary.player.api.ImplPlayModule;
import com.mylibrary.player.api.ImplPlayer;
import com.mylibrary.player.api.StatuChangeListener;
import com.mylibrary.player.constant.Constants;
import com.mylibrary.player.constant.PlayerStatu;
import com.mylibrary.player.util.CommonUtils;

/**
 * UniversalMediaController
 *
 * @since 2021-07-08
 */
public class UniversalMediaController extends ComponentContainer implements ImplPlayModule {
    private static final int CONTROLLER_HIDE_DLEY_TIME = 5000;
    private static final int PROGRESS_RUNNING_TIME = 1000;
    private static final int ANIM_ROTATE = 360;
    private static final int ANIM_DURATION = 2000;
    private static final int ANIM_LOOPED_COUNT = -1;
    private boolean mIsDragMode = false;
    private ImplPlayer mPlayer;
    private DependentLayout topLayout;
    private DirectionalLayout bottomLayout;
    private Image mBack;
    private Image mLoadingImage;
    private DirectionalLayout loadingLayout;
    private DirectionalLayout errorLayout;
    private Image mCenterPlayBtn;
    private Image mPlayToogle;
    private Image mScaleButton;
    private Text mTitleText;
    private Slider mProgressBar;
    private Text mCurrentTime;
    private Text mTotalTime;
    private ControllerHandler mHandler;
    private boolean mIsFullScreen = false;
    private AnimatorProperty mLoadingAnim;
    private long mPlayClickEndTime;
    private StatuChangeListener mStatuChangeListener = new StatuChangeListener() {
        @Override
        public void statuCallback(PlayerStatu statu) {
            mContext.getUITaskDispatcher().asyncDispatch(() -> {
                switch (statu) {
                    case PREPARING:
                        if (mPlayer.getBuilder().isMemoryPlay) {
                            showLoadingLayout();
                            showTileAndBottomControl();
                        }
                        mPlayToogle.setClickable(false);
                        mScaleButton.setEnabled(false);
                        mProgressBar.setEnabled(false);
                        if (!mPlayer.getBuilder().isMemoryPlay) {
                            mProgressBar.setProgressValue(0);
                            mProgressBar.setViceProgress(0);
                        }
                        break;
                    case PREPARED:
                        mProgressBar.setMaxValue(mPlayer.getDuration());
                        mTotalTime.setText(CommonUtils.msToString(mPlayer.getDuration()));
                        break;
                    case PLAY:
                        hideLoadingLayout();
                        hideCenterPlayButton();
                        hideErrorLayout();
                        mPlayToogle.setPixelMap(ResourceTable.Media_ic_music_stop);
                        mPlayToogle.setClickable(true);
                        mScaleButton.setEnabled(true);
                        mProgressBar.setEnabled(true);
                        break;
                    case PAUSE:
                        hideLoadingLayout();
                        mPlayToogle.setClickable(true);
                        mPlayToogle.setPixelMap(ResourceTable.Media_ic_music_play);
                        break;
                    case STOP:
                    case COMPLETE:
                        mPlayToogle.setPixelMap(ResourceTable.Media_ic_music_play);
                        //可能出现二者不等

                        setAdjustCurrentValue();
                        mCenterPlayBtn.setVisibility(VISIBLE);
                        break;
                    case ERROR:
                        showErrorLayout();
                        hideLoadingLayout();
                        break;
                    default:
                        break;
                }
            });
        }
    };
    private boolean mScalable;

    private void showErrorLayout() {
        if (errorLayout.getVisibility() != VERTICAL) {
            errorLayout.setVisibility(VISIBLE);
        }
    }

    /**
     * hideErrorLayout
     */
    public void hideErrorLayout() {
        if (errorLayout.getVisibility() != HIDE) {
            errorLayout.setVisibility(HIDE);
        }
    }

    /**
     * hideCenterPlayButton
     */
    public void hideCenterPlayButton() {
        if (mCenterPlayBtn.getVisibility() != HIDE) {
            mCenterPlayBtn.setVisibility(HIDE);
        }
    }

    /**
     * constructor of SimplePlayerController
     *
     * @param context context
     * @param attrSet attSet
     */
    public UniversalMediaController(Context context, AttrSet attrSet) {
        this(context, attrSet, null);
    }

    /**
     * constructor of SimplePlayerController
     *
     * @param context context
     * @param attrSet attSet
     * @param styleName styleName
     */
    public UniversalMediaController(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        mScalable = CommonUtils.getBooleanValue(attrSet , "uvv_scalable" , true);
        createHandler();
        initView();
        initLoadingAnim();
        initListener();
    }

    /**
     * updatePlayToogleButtonState
     */
    public void updatePlayToogleButtonState() {
        if (mPlayer != null) {
            if (mPlayer.getPlayerStatu() != PlayerStatu.PLAY) {
                mPlayToogle.setPixelMap(ResourceTable.Media_ic_music_play);
            }
        }
    }

    /**
     * show of PlayerLoading
     */
    public void showLoadingLayout() {
        loadingLayout.setVisibility(VISIBLE);
        hideErrorLayout();
        hideCenterPlayButton();
        if (mLoadingAnim.isPaused()) {
            mLoadingAnim.resume();
        } else {
            mLoadingAnim.start();
        }

    }

    /**
     * hide of PlayerLoading
     */
    public void hideLoadingLayout() {
        if (loadingLayout.getVisibility() != HIDE) {
            loadingLayout.setVisibility(HIDE);
            mLoadingAnim.pause();
        }
    }

    private void createHandler() {
        EventRunner runner = EventRunner.create(true);
        if (runner == null) {
            return;
        }
        mHandler = new ControllerHandler(runner);
    }

    private void initView() {
        Component playerController = LayoutScatter.getInstance(mContext).parse(ResourceTable.Layout_simple_player_controller_layout, null, false);
        addComponent(playerController);
        if (playerController.findComponentById(ResourceTable.Id_controller_top_layout) instanceof DependentLayout) {
            topLayout = (DependentLayout) playerController.findComponentById(ResourceTable.Id_controller_top_layout);
        }
        if (playerController.findComponentById(ResourceTable.Id_loading_layout)
            instanceof DirectionalLayout) {
            loadingLayout = (DirectionalLayout) playerController
                .findComponentById(ResourceTable.Id_loading_layout);
        }
        if (playerController.findComponentById(ResourceTable.Id_controller_bottom_layout)
            instanceof DirectionalLayout) {
            bottomLayout = (DirectionalLayout) playerController
                .findComponentById(ResourceTable.Id_controller_bottom_layout);
        }
        if (playerController.findComponentById(ResourceTable.Id_back) instanceof Image) {
            mBack = (Image) playerController.findComponentById(ResourceTable.Id_back);
        }
        if (playerController.findComponentById(ResourceTable.Id_image_loading) instanceof Image) {
            mLoadingImage = (Image) playerController.findComponentById(ResourceTable.Id_image_loading);
        }
        if (playerController.findComponentById(ResourceTable.Id_error_layout) instanceof DirectionalLayout) {
            errorLayout = (DirectionalLayout) playerController.findComponentById(ResourceTable.Id_error_layout);
        }
        if (playerController.findComponentById(ResourceTable.Id_center_play_btn) instanceof Image) {
            mCenterPlayBtn = (Image) playerController.findComponentById(ResourceTable.Id_center_play_btn);
        }
        if (playerController.findComponentById(ResourceTable.Id_play_controller) instanceof Image) {
            mPlayToogle = (Image) playerController.findComponentById(ResourceTable.Id_play_controller);
        }

        if (playerController.findComponentById(ResourceTable.Id_scale_button) instanceof Image) {
            mScaleButton = (Image) playerController.findComponentById(ResourceTable.Id_scale_button);
            mScaleButton.setPixelMap(ResourceTable.Media_uvv_player_scale_btn);
            mScaleButton.setEnabled(false);
        }
        if (mScalable) {
            if (mScaleButton != null) {
                mScaleButton.setVisibility(VISIBLE);
                mScaleButton.setClickedListener(component -> {
                    mIsFullScreen = !mIsFullScreen;
                    int screenOrientation = mIsFullScreen ? AbilityInfo.DisplayOrientation.LANDSCAPE.ordinal()
                        : AbilityInfo.DisplayOrientation.PORTRAIT.ordinal();
                    setFullScreen(mIsFullScreen, screenOrientation);
                });
            }
        } else {
            if (mScaleButton != null) {
                mScaleButton.setVisibility(HIDE);
            }
        }


        if (playerController.findComponentById(ResourceTable.Id_title) instanceof Text) {
            mTitleText = (Text) playerController.findComponentById(ResourceTable.Id_title);
        }

        initProgressAndTimeText(playerController);
    }

    private void initProgressAndTimeText(Component playerController) {
        if (playerController.findComponentById(ResourceTable.Id_progress) instanceof Slider) {
            mProgressBar = (Slider) playerController.findComponentById(ResourceTable.Id_progress);
            mProgressBar.setEnabled(false);
            ShapeElement shapeElement = new ShapeElement();
            shapeElement.setRgbColor(new RgbColor(174, 174, 174));
            shapeElement.setCornerRadius(6.0F);
            mProgressBar.setViceProgressElement(shapeElement);
        }

        if (playerController.findComponentById(ResourceTable.Id_current_time) instanceof Text) {
            mCurrentTime = (Text) playerController.findComponentById(ResourceTable.Id_current_time);
        }
        if (playerController.findComponentById(ResourceTable.Id_end_time) instanceof Text) {
            mTotalTime = (Text) playerController.findComponentById(ResourceTable.Id_end_time);
        }
    }

    private void initLoadingAnim() {
        mLoadingAnim = mLoadingImage.createAnimatorProperty();
        mLoadingAnim.rotate(ANIM_ROTATE).setDuration(ANIM_DURATION).setLoopedCount(ANIM_LOOPED_COUNT);
    }

    private void initListener() {
        topLayout.setTouchEventListener((component, touchEvent) -> true);
        bottomLayout.setTouchEventListener((component, touchEvent) -> true);
        mBack.setClickedListener(component -> {
            //只有在横屏时候显示出来

            mIsFullScreen = !mIsFullScreen;
            int screenOrientation = AbilityInfo.DisplayOrientation.PORTRAIT.ordinal();
            setFullScreen(mIsFullScreen, screenOrientation);
        });
    }

    /**
     * setTitle
     *
     * @param title String
     */
    public void setTitle(String title) {
        mTitleText.setText(title);
    }

    private void initPlayListener() {
        mPlayer.addPlayerStatuCallback(mStatuChangeListener);
        mPlayToogle.setClickedListener(component -> {
            long playClickStartTime = System.currentTimeMillis();
            if (Math.abs(playClickStartTime - mPlayClickEndTime) < 500) {
                return;
            }
            if (mPlayer.isPlaying()) {
                mPlayer.pause();
            } else {
                if (mPlayer.getPlayerStatu() == PlayerStatu.STOP) {
                    mPlayer.replay();
                } else if (mPlayer.getPlayerStatu() == PlayerStatu.PAUSE || mPlayer.getPlayerStatu() == PlayerStatu.COMPLETE || mPlayer.getPlayerStatu() == PlayerStatu.BUFFERING) {
                    mPlayer.resume();
                } else {
                    mPlayer.memoryPlay(false);
                }
            }
            mPlayClickEndTime = System.currentTimeMillis();
        });


        mCenterPlayBtn.setClickedListener(new ClickedListener() {
            @Override
            public void onClick(Component component) {
                if (mPlayer.getPlayerStatu() == PlayerStatu.COMPLETE) {
                    mPlayer.resume();
                } else {
                    mPlayer.memoryPlay(false);
                }
                mCenterPlayBtn.setVisibility(HIDE);
            }
        });

        initProgressBarChangedListener();
    }

    private void initProgressBarChangedListener() {
        mProgressBar.setValueChangedListener(
            new Slider.ValueChangedListener() {
                @Override
                public void onProgressUpdated(Slider slider, int value, boolean isB) {
                    mContext.getUITaskDispatcher().asyncDispatch(() ->
                        mCurrentTime.setText(CommonUtils.msToString(value)));
                }

                @Override
                public void onTouchStart(Slider slider) {
                    mIsDragMode = true;
                    mHandler.removeEvent(Constants.PLAYER_PROGRESS_RUNNING, EventHandler.Priority.IMMEDIATE);
                }

                @Override
                public void onTouchEnd(Slider slider) {
                    mIsDragMode = false;
                    if (slider.getProgress() == mPlayer.getDuration()) {
                        if (mPlayer.getDuration() > 3000) {
                            // 延迟2秒停止

                            mPlayer.rewindTo(mPlayer.getDuration() - 2000);
                        } else {
                            mPlayer.stop();
                        }
                    } else {
                        mPlayer.rewindTo(getBasicTransTime(slider.getProgress()));
                    }
                }
            });
    }

    private int getBasicTransTime(int currentTime) {
        return currentTime / PROGRESS_RUNNING_TIME * PROGRESS_RUNNING_TIME;
    }

    /**
     * setFullScreen
     *
     * @param fullScreen boolean
     * @param screenOrientation int
     */
    public void setFullScreen(boolean fullScreen, int screenOrientation) {
        Ability aility = (Ability) mContext;
        if (fullScreen) {
            aility.getWindow().addFlags(WindowManager.LayoutConfig.MARK_FULL_SCREEN);
            aility.setDisplayOrientation(AbilityInfo.DisplayOrientation.values()[screenOrientation]);
            updateOrientationIcon(AbilityInfo.DisplayOrientation.LANDSCAPE.ordinal());
        } else {
            aility.getWindow().clearFlags(WindowManager.LayoutConfig.MARK_FULL_SCREEN);
            aility.setDisplayOrientation(AbilityInfo.DisplayOrientation.values()[screenOrientation]);
            updateOrientationIcon(AbilityInfo.DisplayOrientation.PORTRAIT.ordinal());
        }
    }

    /**
     * updateOrientationIcon
     *
     * @param orientation int
     */
    public void updateOrientationIcon(int orientation) {
        if (orientation == AbilityInfo.DisplayOrientation.LANDSCAPE.ordinal()) {
            mBack.setVisibility(VISIBLE);
            mScaleButton.setPixelMap(ResourceTable.Media_uvv_star_zoom_in);
        } else {
            mBack.setVisibility(HIDE);
            mScaleButton.setPixelMap(ResourceTable.Media_uvv_player_scale_btn);
        }
    }

    /**
     * showController of PlayerController
     *
     * @param isAutoHide isAutoHide
     */
    public void showController(boolean isAutoHide) {
        mHandler.sendEvent(Constants.PLAYER_CONTROLLER_SHOW, EventHandler.Priority.HIGH);
        if (isAutoHide) {
            hideController(CONTROLLER_HIDE_DLEY_TIME);
        } else {
            mHandler.removeEvent(Constants.PLAYER_CONTROLLER_HIDE);
        }
    }

    /**
     * showController of PlayerController
     */
    public void handlerControllerVisibility() {
        //如果显示状态下，则立即hide，如果hide状态下，则显示，之后，三秒后再hide

        int visibilityStatus = topLayout.getVisibility();
        if (visibilityStatus == VISIBLE) {
            hideController(0);
        } else {
            mHandler.removeEvent(Constants.PLAYER_CONTROLLER_SHOW);
            mHandler.sendEvent(Constants.PLAYER_CONTROLLER_SHOW);
            hideController(CONTROLLER_HIDE_DLEY_TIME);
        }
    }

    private void showTileAndBottomControl() {
        if (topLayout.getVisibility() != VISIBLE) {
            showTileAndBottomControl(true);
        }
        hideController(CONTROLLER_HIDE_DLEY_TIME);
    }

    /**
     * showTileAndBottomControl
     *
     * @param isShow boolean
     */
    public void showTileAndBottomControl(boolean isShow) {
        if (isShow) {
            topLayout.setVisibility(VISIBLE);
            bottomLayout.setVisibility(VISIBLE);
        } else {
            topLayout.setVisibility(HIDE);
            bottomLayout.setVisibility(HIDE);
        }
    }

    private void hideController(int delay) {
        if(mHandler != null){
            mHandler.removeEvent(Constants.PLAYER_CONTROLLER_HIDE);
            mHandler.sendEvent(Constants.PLAYER_CONTROLLER_HIDE, delay, EventHandler.Priority.HIGH);
        }
    }

    /**
     * bind bind player
     *
     * @param player player
     */
    @Override
    public void bind(ImplPlayer player) {
        mPlayer = player;
        initPlayListener();
    }

    /**
     * unbind unbind player
     */
    @Override
    public void unbind() {
        mLoadingAnim.release();
        mHandler.removeAllEvent();
        mHandler = null;

    }

    private class ControllerHandler extends EventHandler {
        private int currentPosition;

        private ControllerHandler(EventRunner runner) {
            super(runner);
        }

        @Override
        public void processEvent(InnerEvent event) {
            super.processEvent(event);
            if (event == null) {
                return;
            }
            switch (event.eventId) {
                case Constants.PLAYER_PROGRESS_RUNNING:
                    if (mPlayer != null && !mIsDragMode) {
                        if (mPlayer.isPlaying() || mPlayer.getPlayerStatu() == PlayerStatu.COMPLETE || mPlayer.getPlayerStatu() == PlayerStatu.BUFFERING) {
                            currentPosition = mPlayer.getCurrentPosition();
                        }
                        mContext.getUITaskDispatcher().asyncDispatch(() -> {
                            mProgressBar.setProgressValue(currentPosition);
                            int bufferedPercentage = mPlayer.getBufferedPercentage();
                            if (bufferedPercentage > 0) {
                                int viceProgress = bufferedPercentage * mPlayer.getDuration() / 100;
                                mProgressBar.setViceProgress(viceProgress);
                            }
                            setAdjustCurrentValue();
                        });
                        mHandler.sendEvent(
                            Constants.PLAYER_PROGRESS_RUNNING, PROGRESS_RUNNING_TIME, Priority.HIGH);
                    }
                    break;
                case Constants.PLAYER_CONTROLLER_HIDE:
                    mContext.getUITaskDispatcher().asyncDispatch(() -> showTileAndBottomControl(false));
                    mHandler.removeEvent(Constants.PLAYER_PROGRESS_RUNNING);
                    break;
                case Constants.PLAYER_CONTROLLER_SHOW:
                    mHandler.removeEvent(Constants.PLAYER_PROGRESS_RUNNING);
                    mHandler.sendEvent(Constants.PLAYER_PROGRESS_RUNNING, Priority.IMMEDIATE);
                    mContext.getUITaskDispatcher().asyncDispatch(() -> {
                        if (topLayout.getVisibility() != VISIBLE) {
                            showTileAndBottomControl(true);
                        }
                    });
                    break;
                default:
                    break;
            }
        }
    }

    private void setAdjustCurrentValue() {
        if (mPlayer != null && mPlayer.getPlayerStatu() == PlayerStatu.COMPLETE) {
            int total = mPlayer.getDuration();
            mCurrentTime.setText(CommonUtils.msToString(total));
            mProgressBar.setProgressValue(total);
        }
    }
}